/*   **********************************************************************  **
 **   Copyright notice                                                       **
 **                                                                          **
 **   (c) 2009, Around(J2)ME				                                 **
 **   All rights reserved.                                                   **
 **                                                                          **
 **   This program and the accompanying materials are made available under   **
 **   the terms of the New BSD License which accompanies this    			 **
 **   distribution.											                 **
 **   A copy is found in the textfile LICENSE.txt							 **
 **                                                                          **
 **   This copyright notice MUST APPEAR in all copies of the file!           **
 **                                                                          **
 **   Main developers:                                                       **
 **     Juri Strumpflohner		http://blog.js-development.com	             **
 **		Matthias Braunhofer		http://matthias.jimdo.com                    **
 **                                                                          **
 **  **********************************************************************  */

package org.aroundme.model;

import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.List;

import org.aroundme.utility.StringReplacer;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
import org.jdom.xpath.XPath;

/**
 * Class responsible for parsing XML requests coming from the client
 */
public class XMLRequestParser {
	private Document doc;

	public XMLRequestParser() {
	}

	/**
	 * Parses the XML document send from the client as String into a DOM model
	 * 
	 * @param xmlString
	 *            = XML Document passed as String
	 * @throws JDOMException
	 * @throws IOException
	 */
	public void loadDocumentModel(String xmlString) throws JDOMException,
			IOException {
		SAXBuilder builder = new SAXBuilder();
		doc = builder.build(new StringReader(xmlString));
	}

	/**
	 * Helper method to execute XPath expressions on the DOM model
	 * 
	 * @param xPath
	 *            = xPath expression to evaluate
	 * @return
	 * @throws Exception
	 */
	private Element getElementByQuery(String xPath) throws Exception {
		if (doc == null) {
			throw new Exception("root has not been initialized");
		}
		try {
			XPath path = XPath.newInstance(xPath);
			Element e = (Element) path.selectSingleNode(doc);
			return e;
		} catch (JDOMException e) {
			e.printStackTrace();
			throw e;
		}
	}

	/**
	 * Returns the type of the XMLDocument. For details see the documentation
	 * 
	 * @return type of the XMLDocument (request for places, commit...)
	 * @throws Exception
	 */
	public String getType() throws Exception {
		Element typeEl = getElementByQuery("//type");
		return typeEl.getText();
	}

	/**
	 * Parses the cell-id out of the XML document
	 * 
	 * @return String representing the cellid
	 * @throws Exception
	 */
	public String getCellid() throws Exception {
		Element cellid = getElementByQuery("//requestBody//cellid");
		return cellid.getText();
	}

	/**
	 * Parses out (from the XML file) the user-login name which is used to
	 * identify a user in the DB
	 * 
	 * @return String containing the username
	 * @throws Exception
	 */
	public String getUserLoginName() throws Exception {
		Element userId = getElementByQuery("//validationData/user");
		return userId.getText();
	}

	/**
	 * Returns the parameter of the requestBody
	 * 
	 * @return parameter of the requestBody
	 * @throws Exception
	 */
	public String getRequestParameter() throws Exception {
		Element paramEl = getElementByQuery("//requestBody/requestParam");
		return paramEl.getText();
	}

	/**
	 * Parses the data-index start out of the XML file
	 * 
	 * @return int indicating the start-index from which the data should be
	 *         started to retrieve
	 * @throws Exception
	 */
	public int getRequestDataBegin() throws Exception {
		Element datBeginEl = getElementByQuery("//requestBody/beginData");
		int result = Integer.parseInt(datBeginEl.getText());
		return result;
	}

	/**
	 * Parses the max-amount of retrieved data out of the XML file
	 * 
	 * @return int representing the amount of data that should be fetched from
	 *         the DB
	 * @throws Exception
	 */
	public int getDataAmount() throws Exception {
		Element datEndEl = getElementByQuery("//requestBody/dataAmount");
		int result = Integer.parseInt(datEndEl.getText());
		return result;
	}

	/**
	 * Parses the encrypted password out of the XML file
	 * 
	 * @return String representing the encrypted password
	 * @throws Exception
	 */
	public String getEncryptedPassword() throws Exception {
		Element validationEl = getElementByQuery("//validationData/password");
		String encrPass = validationEl.getText();

		return encrPass;
	}

	/**
	 * Parses out all places contained in the XML document, creates the
	 * corresponding Place objects and returns them in a Collection
	 * 
	 * @return Collection of Place objects
	 * @throws Exception
	 */
	public ArrayList<Place> getPlaces() throws Exception {
		ArrayList<Place> result = new ArrayList<Place>();

		Element dataEl = getElementByQuery("//requestBody");
		List<Element> places = dataEl.getChildren();
		for (Element element : places) {
			Place p = new Place();
			String id = element.getChildText("id");
			if (id != null && !id.equals("") && !id.equals("null"))
				p.setId(Integer.parseInt(id));
			p.setName(revertToOriginal(element.getChildText("name")));
			p.setAddress(revertToOriginal(element.getChildText("address")));
			p.setCity(revertToOriginal(element.getChildText("city")));
			p.setPhone(revertToOriginal(element.getChildText("phone")));
			
			Element categoriesDataEl = element.getChild("categories");
			List<Element> categoriesEl = categoriesDataEl.getChildren();
			for (Element categoryEl : categoriesEl) {
				Category category = getCategory(categoryEl);
			}
			
			User us = new User();
			us.setLoginName(element.getChildText("user"));
			p.setUser(us);

			Element locEl = element.getChild("location");

			// There exists no location element for delete requests
			if (locEl != null) {
				p.setLocation(getLocation(locEl));
			}
			result.add(p);
		}

		return result;
	}
	
	private Location getLocation(Element locationEl){
		Location loc = new Location();
		loc.setCellid(locationEl.getChildText("cellid"));
		loc.setLat(locationEl.getChildText("lat"));
		loc.setLon(locationEl.getChildText("lon"));
		
		return loc;
	}
	
	private Category getCategory(Element categoryXMLElement){
		Category result = new Category();
		
		result.setName(categoryXMLElement.getChildText("category"));
		
		return result;
	}
	
	/**
	 * Retrieves the requested location
	 * @return
	 * @throws Exception
	 */
	public Location getRequestedLocation() throws Exception{
		Element requestBodyEl = getElementByQuery("//requestBody/location");
		
		Location location = getLocation(requestBodyEl);
		return location;
	}
	
	/**
	 * returns the category that has been encoded in the XML request coming
	 * from the client
	 * @return
	 * @throws Exception
	 */
	public String getRequestedCategory() throws Exception{
		Element requestBodyEl = getElementByQuery("//requestBody");
		return getCategory(requestBodyEl).getName();
	}
	
	


	/**
	 * Parses out a comment from the XML-file
	 * 
	 * @return Comment object representing a place comment
	 * @throws Exception
	 */
	public Comment getComment() throws Exception {
		Comment result = new Comment();
		Element commentEl = getElementByQuery("//comment");
		// add a dummy place to the comment for later DB retrieval
		Place p = new Place();
		String placeid = commentEl.getChildText("placeid");
		if (placeid != null && !placeid.equals("")) {
			p.setId(Integer.parseInt(placeid));
		}
		User u = new User();
		String loginName = getUserLoginName();
		if (loginName != null && !loginName.equals("")) {
			u.setLoginName(loginName);
		}
		result.setPlace(p);
		result.setUser(u);
		result.setComment(commentEl.getChildText("content"));

		return result;
	}

	/**
	 * Parses out the search-query from the XML file
	 * 
	 * @return String representing the search query
	 * @throws Exception
	 */
	public String getSearchQuery() throws Exception {
		String query = null;

		Element searchElement = getElementByQuery("//requestBody/searchQuery");
		query = searchElement.getText();
		return query;
	}

	/**
	 * Reverts critical chars
	 * 
	 * @param text
	 *            String, containing subsituted critical chars
	 * @return String with original chars
	 */
	private String revertToOriginal(String text) {
		return new StringReplacer().replaceCriticalSymbols(text);
	}

}
